package com.kamistoat.meimeistore.cart.interceptor;

import com.kamistoat.common.To.LoginSuccessTo;
import com.kamistoat.common.constant.AuthConstant;
import com.kamistoat.common.constant.CartConstant;
import com.kamistoat.meimeistore.cart.vo.UserInfoVo;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.UUID;

/**
 * 购物车拦截器，在执行目标方法前，判断用户登录状态
 * 并将登录状态封装传递给目标方法
 * 拦截器是一个组件，且必须实现HandlerInterceptor接口方法
 */
@Component
public class CartInterceptor implements HandlerInterceptor {
    // 一个用于保存UserInfoVo的ThreadLocal
    public static ThreadLocal<UserInfoVo> threadLocal = new ThreadLocal<>();
    // 用于记录当前用户是否是第一次使用离线购物车，默认不是
    private Boolean NEW_TEMP;

    /**
     * 覆写的第一个方法。其实就是拦截器本身。在Controller之前执行
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 每次都默认不是第一次使用离线购物车
        NEW_TEMP = false;
        // 准备一个Vo，无论用户是否登录都将会返回这个VO回去
        UserInfoVo userInfoVo = new UserInfoVo();
        // 从拦截的请求中将session拿过来
        HttpSession session = request.getSession();
        // 因为本身拦截的session就是要获取loginUser，所以这里也是这样做
        // 而且其实session中封装的就是LoginSuccessTo，直接强转
        LoginSuccessTo loginSuccessTo = (LoginSuccessTo) session.getAttribute(AuthConstant.LOGINUSER_KEY);

        /**
         * 根据不同的情况做出不同的响应
         */
        // 如果用户直接是登录了的
        // 那么直接将用户的Id封装到Vo中
        if (loginSuccessTo != null) {
            userInfoVo.setUserId(loginSuccessTo.getId());
        }
        // 不管是否登录，都检查是否有cookie
        // 就去看看所有的cookie中是否有一个名为user-key的cookie
        Cookie[] cookies = request.getCookies();  // 使用HttpServletRequest.getCookies()获取所有的cookie
        if (cookies != null && cookies.length > 0) {
            // 没有简单方法，只能遍历所有cookie
            for (Cookie cookie : cookies) {
                String cookieName = cookie.getName();  // 使用Cookie.getName()获取cookie的名字
                if (cookieName.equals(CartConstant.TEMP_USER_COOKIE_NAME)) {
                    // 如果已经携带了名为user-key的cookie，就将这个cookie封装到Vo中
                    userInfoVo.setUserKey(cookie.getValue());  // 使用Cookie.getValue()获取cookie的值
                }
            }
        }
        // 如果遍历完所有cookie都没有找到名为user-key的cookie，说明这个用户是第一次使用离线购物车，那么就得生成一个user-key来标识离线购物车
        if (StringUtils.isEmpty(userInfoVo.getUserKey())) {
            String uuid = UUID.randomUUID().toString();
            userInfoVo.setUserKey(uuid);
            NEW_TEMP = true;  // 标识其为第一次使用离线购物车
        }

        // 将要返回的Vo直接用过ThreadLocal存放。不需要指定map的键，因为键就是当前线程的线程号。
        threadLocal.set(userInfoVo);

        // 无论用户登录与否，拦截器都不会终止请求，而是全部放行回去
        return true;
    }


    /**
     * 覆写的第二个方法，在Controller之后但在页面渲染前执行
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 从ThreadLocal中把当前线程的Vo拿出来
        UserInfoVo userInfoVo = threadLocal.get();
        if (NEW_TEMP || (!NEW_TEMP && CartConstant.TEMP_USER_COOKIE_REFRESH)) {
            // 如果是第一次使用临时购物车，那必然是要将cookie进行保存的
            // 如果不是第一次，那么要看设置是否允许重复更新
            Cookie cookie = new Cookie(CartConstant.TEMP_USER_COOKIE_NAME, userInfoVo.getUserKey());
            cookie.setDomain("meimeistore.com");
            cookie.setMaxAge(CartConstant.TEMP_USER_COOKIE_AGE);
            response.addCookie(cookie);
        }
    }
}
